﻿using System;
using System.Collections.Generic;

namespace Sixeyed.CodeGeneration.Metadata
{
    /// <summary>
    /// Configuration for a specific metadata source and provider
    /// </summary>
    /// <remarks>
    /// Allows consumers to persist and reload a populated source and provider
    /// </remarks>
    public class SourceConfiguration
    {
        /// <summary>
        /// Type of source
        /// </summary>
        public string SourceTypeName { get; set; }

        /// <summary>
        /// Name of source
        /// </summary>
        public string SourceName { get; set; }

        /// <summary>
        /// Uri of source
        /// </summary>
        public string SourceUri { get; set; }

        /// <summary>
        /// Name of metadata item provider
        /// </summary>
        public string ProviderName { get; set; }

        /// <summary>
        /// Validates that source configuration is complete for loading metadata
        /// </summary>
        public void Validate()
        {
            List<Arguments> validArguments = new List<Arguments>(4);
            validArguments.Add(Arguments.None);
            validArguments.Add(Arguments.SourceNameAndAbove);
            validArguments.Add(Arguments.SourceUriAndAbove);
            validArguments.Add(Arguments.ProviderNameAndAbove);
            this.Validate(validArguments);
        }

        /// <summary>
        /// Validates that source configuration is complete for loading metadata
        /// </summary>
        /// <param name="validArguments">Valid argument conditions</param>
        public void Validate(List<Arguments> validArguments)
        {
            Arguments args = Arguments.None;
            CheckArgument(this.SourceTypeName, Arguments.SourceTypeName, ref args);
            CheckArgument(this.SourceName, Arguments.SourceName, ref args);
            CheckArgument(this.SourceUri, Arguments.SourceUri, ref args);
            CheckArgument(this.ProviderName, Arguments.ProviderName, ref args);

            if (!validArguments.Contains(args))
            {
                if (args == Arguments.None)
                {
                    throw new ArgumentException("SourceTypeName, SourceName, SourceUri and ProviderName must be specified", "SourceConfiguration");
                }
                else if (args == Arguments.SourceTypeName)
                {
                    throw new ArgumentException("SourceName, SourceUri and ProviderName must be specified", "SourceConfiguration");
                }
                else if ((args & Arguments.ProviderName) == Arguments.ProviderName)
                {
                    throw new ArgumentException("ProviderName cannot be specified without SourceTypeName, SourceName and SourceUri", "SourceConfiguration");
                }
                else if ((args & Arguments.SourceUri) == Arguments.SourceUri)
                {
                    throw new ArgumentException("SourceUri cannot be specified without SourceTypeName and SourceName", "SourceConfiguration");
                }
                else if ((args & Arguments.SourceName) == Arguments.SourceName)
                {
                    throw new ArgumentException("SourceName cannot be specified without SourceTypeName", "SourceConfiguration");
                }
            }
        }

        private void CheckArgument(string argumentValue, Arguments argumentToCheck, ref Arguments checkedArguments)
        {
            if (!string.IsNullOrEmpty(argumentValue))
            {
                checkedArguments = checkedArguments | argumentToCheck;
                checkedArguments = checkedArguments &~ Arguments.None;
            }
        }

        /// <summary>
        /// Represents source configuration settings and combinations
        /// </summary>
        [Flags]
        public enum Arguments
        {
            None = 1,
            SourceTypeName = 2,
            SourceName = 4,
            SourceUri = 8,
            ProviderName = 16,
            ProviderNameAndAbove = ProviderName | SourceUri | SourceName | SourceTypeName,
            SourceUriAndAbove = SourceUri | SourceName | SourceTypeName,
            SourceNameAndAbove = SourceName | SourceTypeName
        }
    }
}
